﻿using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Vk.Methods.Common;
using Vk.Methods.Wall;

namespace Vk.Methods.Market
{
    public class Market
    {
        private readonly Api _api;

        public Market(Api api)
        {
            _api = api;
        }

        /// <summary>
        ///     Возвращает список товаров в сообществе.
        /// </summary>
        /// <param name="ownerId">Идентификатор владельца товаров.  </param>
        /// <param name="albumId">Идентификатор подборки, товары из которой нужно вернуть. </param>
        /// <param name="count">Количество возвращаемых товаров. </param>
        /// <param name="offset">Смещение относительно первого найденного товара для выборки определенного подмножества. </param>
        /// <param name="extended">
        ///     true — будут возвращены дополнительные поля likes, can_comment, can_repost, photos, views_count.
        ///     По умолчанию эти поля не возвращается.
        /// </param>
        public async Task<ListCount<MarketItem>> Get(int ownerId, int? albumId = null, int? count = null,
            int? offset = null, bool? extended = null)
        {
            var query = new ApiQuery("market.get")
                .Add("owner_id", ownerId)
                .Add("album_id", albumId)
                .Add("count", count)
                .Add("offset", offset)
                .Add("extended", extended);

            var result = await _api.Manager.Execute(query);

            return _api.Manager.MethodSuccessed
                ? new ListCount<MarketItem>(result, apiNode => new MarketItem(apiNode))
                : null;
        }

        /// <summary>
        ///     Возвращает информацию о товарах по идентификаторам.
        /// </summary>
        /// <param name="itemIds">
        ///     Перечисленные через запятую идентификаторы — идущие через знак подчеркивания id владельцев
        ///     товаров и id самих товаров. Если товар принадлежит сообществу, то в качестве первого параметра используется -id
        ///     сообщества.
        /// </param>
        /// <param name="extended">
        ///     1 — будут возвращены дополнительные поля likes, can_comment, can_repost, photos, views_count. По
        ///     умолчанию: 0.
        /// </param>
        public async Task<ListCount<MarketItem>> GetById(IEnumerable<string> itemIds, bool? extended = null)
        {
            if (itemIds == null) throw new ArgumentNullException(nameof(itemIds));

            var query = new ApiQuery("market.getById")
                .Add("item_ids", itemIds)
                .Add("extended", extended);

            var result = await _api.Manager.Execute(query);

            return _api.Manager.MethodSuccessed
                ? new ListCount<MarketItem>(result, apiNode => new MarketItem(apiNode))
                : null;
        }

        /// <summary>
        ///     Ищет товары в каталоге сообщества.
        /// </summary>
        /// <param name="ownerId">Идентификатор сообщества, которому принадлежат товары. </param>
        /// <param name="albumId">Идентификатор подборки, товары из которой нужно вернуть. </param>
        /// <param name="q">Строка поискового запроса. Например, зеленые тапочки. </param>
        /// <param name="priceFrom">Минимальное значение цены товаров в сотых долях единицы валюты. Например, 100000. </param>
        /// <param name="priceTo">Максимальное значение цены товаров  в сотых долях единицы валюты. Например, 1410000. </param>
        /// <param name="tags">Перечисленные через запятую идентификаторы меток. </param>
        /// <param name="sort">
        ///     Вид сортировки.  0 — пользовательская расстановка,  1 — по дате добавления товара, 2 — по цене, 3 —
        ///     по популярности.
        /// </param>
        /// <param name="rev">0 — не использовать обратный порядок, 1 — использовать обратный порядок. </param>
        /// <param name="offset">Смещение относительно первого найденного товара для выборки определенного подмножества. </param>
        /// <param name="count">Количество возвращаемых товаров. </param>
        /// <param name="extended">
        ///     1 — будут возвращены дополнительные поля likes, can_comment, can_repost, photos, views_count. По
        ///     умолчанию эти поля не возвращается.
        /// </param>
        public async Task<ListCount<MarketItem>> Search(int ownerId, int? albumId = null, string q = null,
            int? priceFrom = null, int? priceTo = null, IEnumerable<string> tags = null,
            ApiEnum<MarketItemSort> sort = null,
            bool? rev = null, int? offset = null,
            int? count = null, bool? extended = null)
        {
            var query = new ApiQuery("market.search")
                .Add("owner_id", ownerId)
                .Add("album_id", albumId)
                .Add("q", q)
                .Add("price_from", priceFrom)
                .Add("price_to", priceTo)
                .Add("tags", tags)
                .Add("sort", sort?.Value)
                .Add("rev", rev)
                .Add("offset", offset)
                .Add("count", count)
                .Add("extended", extended);

            var result = await _api.Manager.Execute(query);

            return _api.Manager.MethodSuccessed
                ? new ListCount<MarketItem>(result, apiNode => new MarketItem(apiNode))
                : null;
        }

        /// <summary>
        /// Возвращает список подборок с товарами. 
        /// </summary>
        /// <param name="ownerId">Идентификатор владельца товаров.  </param>
        /// <param name="offset">Смещение относительно первой найденной подборки для выборки определенного подмножества. </param>
        /// <param name="count">Количество возвращаемых подборок. </param>
        public async Task<ListCount<MarketAlbum>> GetAlbums(int ownerId, int? offset = null, int? count = null)
        {
            var query = new ApiQuery("market.getAlbums")
            .Add("owner_id", ownerId)
            .Add("offset", offset)
            .Add("count", count);

            var result = await _api.Manager.Execute(query);

            return _api.Manager.MethodSuccessed ? new ListCount<MarketAlbum>(result, apiNode => new MarketAlbum(apiNode)) : null;
        }

        /// <summary>
        /// Возвращает данные подборки с товарами. 
        /// </summary>
        /// <param name="ownerId">Идентификатор владельца подборки.  </param>
        /// <param name="albumIds">Идентификаторы подборок, данные о которых нужно получить. </param>
        public async Task<ListCount<MarketAlbum>> GetAlbumById(int ownerId, IEnumerable<int> albumIds)
        {
            var query = new ApiQuery("market.getAlbumById")
            .Add("owner_id", ownerId)
            .Add("album_ids", albumIds);

            var result = await _api.Manager.Execute(query);

            return _api.Manager.MethodSuccessed ? new ListCount<MarketAlbum>(result, apiNode => new MarketAlbum(apiNode)) : null;
        }

        /// <summary>
        /// Создает новый комментарий к товару. 
        /// </summary>
        /// <param name="ownerId">Идентификатор владельца товара.  </param>
        /// <param name="itemId">Идентификатор товара. </param>
        /// <param name="message">Текст комментария (является обязательным, если не задан параметр attachments). </param>
        /// <param name="attachments">Список объектов, приложенных к комментарию и разделённых символом &quot;,&quot;. Поле attachments представляется в формате:&lt;type&gt; — тип медиа-вложения:&lt;owner_id&gt; — идентификатор владельца медиа-вложения &lt;media_id&gt; — идентификатор медиа-вложения. Например:Параметр является обязательным, если не задан параметр message. </param>
        /// <param name="fromGroup">1 — комментарий будет опубликован от имени группы, 0 — комментарий будет опубликован от имени пользователя (по умолчанию). </param>
        /// <param name="replyToComment">Идентификатор комментария, в ответ на который должен быть добавлен новый комментарий. </param>
        /// <param name="stickerId">Идентификатор стикера. </param>
        /// <param name="guid">Уникальный идентификатор, предназначенный для предотвращения повторной отправки одинакового комментария. </param>
        public async Task<int> CreateComment(int ownerId, int itemId, string message, IEnumerable<WallAttachment> attachments, bool? fromGroup = null, int? replyToComment = null, int? stickerId = null, string guid = null)
        {
            var query = new ApiQuery("market.createComment")
            .Add("owner_id", ownerId)
            .Add("item_id", itemId)
            .Add("message", message)
            .Add("attachments", attachments)
            .Add("from_group", fromGroup)
            .Add("reply_to_comment", replyToComment)
            .Add("sticker_id", stickerId)
            .Add("guid", guid);

            var result = await _api.Manager.Execute(query);

            return _api.Manager.MethodSuccessed ? result.Int().Value : -1;
        }

        /// <summary>
        /// Возвращает список комментариев к товару. 
        /// </summary>
        /// <param name="ownerId">Идентификатор владельца товара.  </param>
        /// <param name="itemId">Идентификатор товара. </param>
        /// <param name="needLikes">1 — возвращать информацию о лайках. </param>
        /// <param name="startCommentId">Идентификатор комментария, начиная с которого нужно вернуть список (подробности см. ниже). </param>
        /// <param name="offset">Сдвиг, необходимый для получения конкретной выборки результатов. </param>
        /// <param name="count">Число комментариев, которые необходимо получить. </param>
        /// <param name="sort">Порядок сортировки комментариев (asc — от старых к новым, desc - от новых к старым) </param>
        /// <param name="extended">1 — комментарии в ответе будут возвращены в виде пронумерованных объектов, дополнительно будут возвращены списки объектов profiles, groups. </param>
        /// <param name="fields">Список дополнительных полей профилей, которые необходимо вернуть. См. . Доступные значения: sex, bdate, city, country, photo_50, photo_100, photo_200_orig, photo_200, photo_400_orig, photo_max, photo_max_orig, photo_id, online, online_mobile, domain, has_mobile, contacts, connections, site, education, universities, schools, can_post, can_see_all_posts, can_see_audio, can_write_private_message, status, last_seen, common_count, relation, relatives, counters, screen_name, maiden_name, timezone, occupation,activities, interests, music, movies, tv, books, games, about, quotes, personal, friend_status, military, career </param>
        public async Task<ListCount<Comment>> GetComments(int ownerId, int itemId, bool? needLikes = null, int? startCommentId = null, int? offset = null, int? count = null, SortOrder sort = null, bool? extended = null, IEnumerable<string> fields  =null)
        {
            var query = new ApiQuery("market.getComments")
                .Add("owner_id", ownerId)
                .Add("item_id", itemId)
                .Add("need_likes", needLikes)
                .Add("start_comment_id", startCommentId)
                .Add("offset", offset)
                .Add("count", count)
                .Add("sort", sort?.StringValue)
                .Add("extended", extended)
                .Add("fields", fields);

            var result = await _api.Manager.Execute(query);

            return _api.Manager.MethodSuccessed ? new ListCount<Comment>(result, apiNode => new Comment(apiNode)) : null;
        }

        /// <summary>
        /// Удаляет комментарий к товару. 
        /// </summary>
        /// <param name="ownerId">Идентификатор владельца товара.  </param>
        /// <param name="commentId">Идентификатор комментария. </param>
        public async Task<bool> DeleteComment(int ownerId, int commentId)
        {
            var query = new ApiQuery("market.deleteComment")
            .Add("owner_id", ownerId)
            .Add("comment_id", commentId);

            var result = await _api.Manager.Execute(query);

            return _api.Manager.MethodSuccessed && result.Bool().Value;
        }

        /// <summary>
        /// Восстанавливает удаленный комментарий к товару. 
        /// </summary>
        /// <param name="ownerId">Идентификатор владельца товара.  </param>
        /// <param name="commentId">Идентификатор удаленного комментария. </param>
        public async Task<bool> RestoreComment(int ownerId, int commentId)
        {
            var query = new ApiQuery("market.restoreComment")
            .Add("owner_id", ownerId)
            .Add("comment_id", commentId);

            var result = await _api.Manager.Execute(query);

            return _api.Manager.MethodSuccessed && result.Bool().Value;
        }

        /// <summary>
        /// Изменяет текст комментария к товару. 
        /// </summary>
        /// <param name="ownerId">Идентификатор владельца товара.  </param>
        /// <param name="commentId">Идентификатор комментария. </param>
        /// <param name="message">Новый текст комментария (является обязательным, если не задан параметр attachments). Максимальное количество символов: 2048. </param>
        /// <param name="attachments">Новый список объектов, приложенных к комментарию и разделённых символом &quot;,&quot;. Поле attachments представляется в формате:&lt;type&gt; — тип медиа-вложения:&lt;owner_id&gt; — идентификатор владельца медиа-вложения &lt;media_id&gt; — идентификатор медиа-вложения. Например:Параметр является обязательным, если не задан параметр message. </param>
        public async Task<bool> EditComment(int ownerId, int commentId, string message = null, IEnumerable<WallAttachment> attachments = null)
        {
            var query = new ApiQuery("market.editComment")
            .Add("owner_id", ownerId)
            .Add("comment_id", commentId)
            .Add("message", message)
            .Add("attachments", attachments);

            var result = await _api.Manager.Execute(query);

            return _api.Manager.MethodSuccessed && result.Bool().Value;
        }

        /// <summary>
        /// Позволяет оставить жалобу на комментарий к товару. 
        /// </summary>
        /// <param name="ownerId">Идентификатор владельца товара.  </param>
        /// <param name="commentId">Идентификатор комментария. </param>
        /// <param name="reason">Причина жалобы </param>
        public async Task<bool> ReportComment(int ownerId, int commentId, ReportReason reason)
        {
            var query = new ApiQuery("market.reportComment")
            .Add("owner_id", ownerId)
            .Add("comment_id", commentId)
            .Add("reason", reason?.Value);

            var result = await _api.Manager.Execute(query);

            return _api.Manager.MethodSuccessed && result.Bool().Value;
        }

        /// <summary>
        /// Возвращает список категорий для товаров. 
        /// </summary>
        /// <param name="count">Количество категорий, информацию о которых необходимо вернуть. </param>
        /// <param name="offset">Смещение, необходимое для выборки определенного подмножества категорий. </param>
        public async Task<ListCount<MarketCategory>> GetCategories(int? count = null, int? offset = null)
        {
            var query = new ApiQuery("market.getCategories")
            .Add("count", count)
            .Add("offset", offset);

            var result = await _api.Manager.Execute(query);

            return _api.Manager.MethodSuccessed ? new ListCount<MarketCategory>(result, apiNode => new MarketCategory(apiNode)) : null;
        }

        /// <summary>
        /// Позволяет отправить жалобу на товар. 
        /// </summary>
        /// <param name="ownerId">Идентификатор владельца товаров.  </param>
        /// <param name="itemId">Идентификатор товара </param>
        /// <param name="reason">Причина жалобы </param>
        public async Task<bool> Report(int ownerId, int itemId, ReportReason reason)
        {
            var query = new ApiQuery("market.report")
            .Add("owner_id", ownerId)
            .Add("item_id", itemId)
            .Add("reason", reason?.Value);

            var result = await _api.Manager.Execute(query);

            return _api.Manager.MethodSuccessed && result.Bool().Value;
        }

        /// <summary>
        /// Добавляет новый товар. 
        /// </summary>
        /// <param name="ownerId">Идентификатор владельца товара.  </param>
        /// <param name="name">Название товара. Ограничение по длине считается в кодировке cp1251. </param>
        /// <param name="description">Описание товара. </param>
        /// <param name="categoryId">Идентификатор категории товара. </param>
        /// <param name="price">Цена товара. </param>
        /// <param name="deleted">Статус товара (1 — товар удален, 0 — товар не удален). </param>
        /// <param name="mainPhotoId">Идентификатор фотографии обложки товара. Фотография должна быть загружена с помощью метода , передав параметр main_photo. См.  о загрузке фотографии товаров </param>
        /// <param name="photoIds">Идентификаторы дополнительных фотографий товара. Фотография должна быть загружена с помощью метода . См.  о загрузке фотографии товаров </param>
        public async Task<int> Add(int ownerId, string name, string description, int categoryId, int price, int mainPhotoId, bool? deleted = null, IEnumerable<int> photoIds = null)
        {
            var query = new ApiQuery("market.add")
                .Add("owner_id", ownerId)
                .Add("name", name)
                .Add("description", description)
                .Add("category_id", categoryId)
                .Add("price", price)
                .Add("deleted", deleted)
                .Add("main_photo_id", mainPhotoId)
                .Add("photo_ids", photoIds);

            var result = await _api.Manager.Execute(query);

            return _api.Manager.MethodSuccessed ? result.Int().Value : -1;
        }

        /// <summary>
        /// Редактирует товар. 
        /// </summary>
        /// <param name="ownerId">Идентификатор владельца товара.  </param>
        /// <param name="itemId">Идентификатор товара. </param>
        /// <param name="name">Новое название товара. </param>
        /// <param name="description">Новое описание товара. </param>
        /// <param name="categoryId">Идентификатор категории товара. </param>
        /// <param name="price">Цена товара. </param>
        /// <param name="deleted">Статус товара (1 — товар удален, 0 — товар не удален). </param>
        /// <param name="mainPhotoId">Идентификатор фотографии для обложки товара. Фотография должна быть загружена с помощью метода , передав параметр main_photo. См.  о загрузке фотографии товаров. </param>
        /// <param name="photoIds">Идентификаторы дополнительных фотографией товара. Фотография должна быть загружена с помощью метода . См.  о загрузке фотографии товаров. </param>
        public async Task<bool> Edit(int ownerId, int itemId, string name, string description, int categoryId, float price, int mainPhotoId, IEnumerable<int> photoIds = null, bool? deleted = null)
        {
            var query = new ApiQuery("market.edit")
                .Add("owner_id", ownerId)
                .Add("item_id", itemId)
                .Add("name", name)
                .Add("description", description)
                .Add("category_id", categoryId)
                .Add("price", price)
                .Add("deleted", deleted)
                .Add("main_photo_id", mainPhotoId)
                .Add("photo_ids", photoIds);

            var result = await _api.Manager.Execute(query);

            return _api.Manager.MethodSuccessed && result.Bool().Value;
        }

        /// <summary>
        /// Удаляет товар. 
        /// </summary>
        /// <param name="ownerId">Идентификатор владельца товара.  </param>
        /// <param name="itemId">Идентификатор товара. </param>
        public async Task<bool> Delete(int ownerId, int itemId)
        {
            var query = new ApiQuery("market.delete")
                .Add("owner_id", ownerId)
                .Add("item_id", itemId);

            var result = await _api.Manager.Execute(query);

            return _api.Manager.MethodSuccessed && result.Bool().Value;
        }

        /// <summary>
        /// Восстанавливает удаленный товар. 
        /// </summary>
        /// <param name="ownerId">Идентификатор владельца товара.  </param>
        /// <param name="itemId">Идентификатор товара. </param>
        public async Task<bool> Restore(int ownerId, int itemId)
        {
            var query = new ApiQuery("market.restore")
                .Add("owner_id", ownerId)
                .Add("item_id", itemId);

            var result = await _api.Manager.Execute(query);

            return _api.Manager.MethodSuccessed && result.Bool().Value;
        }

        /// <summary>
        /// Изменяет положение товара в подборке. 
        /// </summary>
        /// <param name="ownerId">Идентификатор владельца товара.  </param>
        /// <param name="albumId">Идентификатор подборки, в которой находится товар.  0 — для сортировки общего списка товаров. </param>
        /// <param name="itemId">Идентификатор товара. </param>
        /// <param name="before">Идентификатор товара, перед которым следует поместить текущий. </param>
        /// <param name="after">Идентификатор товара, после которого следует поместить текущий. </param>
        public async Task<bool> ReorderItems(int ownerId, int itemId, int? albumId = null, int? before = null, int? after = null)
        {
            var query = new ApiQuery("market.reorderItems")
                .Add("owner_id", ownerId)
                .Add("album_id", albumId)
                .Add("item_id", itemId)
                .Add("before", before)
                .Add("after", after);

            var result = await _api.Manager.Execute(query);

            return _api.Manager.MethodSuccessed && result.Bool().Value;
        }

        /// <summary>
        /// Изменяет положение подборки с товарами в списке. 
        /// </summary>
        /// <param name="ownerId">Идентификатор владельца альбома.  </param>
        /// <param name="albumId">Идентификатор подборки. </param>
        /// <param name="before">Идентификатор подборки, перед которой следует поместить текущую. </param>
        /// <param name="after">Идентификатор подборки, после которой следует поместить текущую. </param>
        public async Task<bool> ReorderAlbums(int ownerId, int albumId, int? before = null, int? after = null)
        {
            var query = new ApiQuery("market.reorderAlbums")
                .Add("owner_id", ownerId)
                .Add("album_id", albumId)
                .Add("before", before)
                .Add("after", after);

            var result = await _api.Manager.Execute(query);

            return _api.Manager.MethodSuccessed && result.Bool().Value;
        }

        /// <summary>
        /// Добавляет новую подборку с товарами. 
        /// </summary>
        /// <param name="ownerId">Идентификатор владельца подборки.  </param>
        /// <param name="title">Название подборки. </param>
        /// <param name="photoId">Идентификатор фотографии-обложки подборки. Фотография должна быть загружена с помощью метода . См.  о загрузке фотографии товаров. </param>
        /// <param name="mainAlbum">Назначить подборку основной (1 — назначить, 0 — нет). </param>
        public async Task<int> AddAlbum(int ownerId, string title, int? photoId = null, bool? mainAlbum = null)
        {
            if (title == null) throw new ArgumentNullException(nameof(title));

            var query = new ApiQuery("market.addAlbum")
                .Add("owner_id", ownerId)
                .Add("title", title)
                .Add("photo_id", photoId)
                .Add("main_album", mainAlbum);

            var result = await _api.Manager.Execute(query);

            return _api.Manager.MethodSuccessed ? result.Int().Value : -1;
        }

        /// <summary>
        /// Редактирует подборку с товарами. 
        /// </summary>
        /// <param name="ownerId">Идентификатор владельца подборки.  </param>
        /// <param name="albumId">Идентификатор подборки. </param>
        /// <param name="title">Название подборки. </param>
        /// <param name="photoId">Идентификатор фотографии-обложки подборки. Фотография должна быть загружена с помощью метода . См.  о загрузке фотографии товаров. </param>
        /// <param name="mainAlbum">Назначить подборку основной (1 — назначить, 0 — нет). </param>
        public async Task<bool> EditAlbum(int ownerId, int albumId, string title, int? photoId = null, bool? mainAlbum = null)
        {
            var query = new ApiQuery("market.editAlbum")
                .Add("owner_id", ownerId)
                .Add("album_id", albumId)
                .Add("title", title)
                .Add("photo_id", photoId)
                .Add("main_album", mainAlbum);

            var result = await _api.Manager.Execute(query);

            return _api.Manager.MethodSuccessed && result.Bool().Value;
        }

        /// <summary>
        /// Удаляет подборку с товарами. 
        /// </summary>
        /// <param name="ownerId">Идентификатор владельца подборки.  </param>
        /// <param name="albumId">Идентификатор подборки. </param>
        public async Task<bool> DeleteAlbum(int ownerId, int albumId)
        {
            var query = new ApiQuery("market.deleteAlbum")
                .Add("owner_id", ownerId)
                .Add("album_id", albumId);

            var result = await _api.Manager.Execute(query);

            return _api.Manager.MethodSuccessed && result.Bool().Value;
        }

        /// <summary>
        /// Удаляет товар из одной или нескольких выбранных подборок. 
        /// </summary>
        /// <param name="ownerId">Идентификатор владельца товара.  </param>
        /// <param name="itemId">Идентификатор товара. </param>
        /// <param name="albumIds">Идентификаторы подборок, из которых нужно удалить товар. </param>
        public async Task<bool> RemoveFromAlbum(int ownerId, int itemId, IEnumerable<int> albumIds)
        {
            var query = new ApiQuery("market.removeFromAlbum")
                .Add("owner_id", ownerId)
                .Add("item_id", itemId)
                .Add("album_ids", albumIds);

            var result = await _api.Manager.Execute(query);

            return _api.Manager.MethodSuccessed && result.Bool().Value;
        }

        /// <summary>
        /// Добавляет товар в одну или несколько выбранных подборок. 
        /// </summary>
        /// <param name="ownerId">Идентификатор владельца товара.  </param>
        /// <param name="itemId">Идентификатор товара. </param>
        /// <param name="albumIds">Идентификаторы подборок, в которые нужно добавить товар. </param>
        public async Task<bool> AddToAlbum(int ownerId, int itemId, IEnumerable<int> albumIds)
        {
            var query = new ApiQuery("market.addToAlbum")
                .Add("owner_id", ownerId)
                .Add("item_id", itemId)
                .Add("album_ids", albumIds);

            var result = await _api.Manager.Execute(query);

            return _api.Manager.MethodSuccessed && result.Bool().Value;
        }

    }
}